package cn.edu.gdut.deepcode.ws.endpoint;

import cn.edu.gdut.deepcode.utils.MessageUtils;
import cn.edu.gdut.deepcode.context.UserContext;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.io.IOException;

/**
 * <p>
 * 使用WebSocket开启协同编辑功能
 * </p>
 *
 * @Author: myc
 * @Since: 2025-04-14  22:54
 * @Version: 1.0.0
 */
@ServerEndpoint(value = "/collaborativeEditing/{canvasId}"
                    /*, configurator = GetHttpSessionConfigurator.class*/)
@Component
public class CollaborativeEditingEndpoint {

    /*
    // 存储已经建立WebSocket连接的Session对象
    private static final Map<String, Session> onlineUsers = new ConcurrentHashMap<>();

    private HttpSession httpSession;
    */

    static Log log = LogFactory.get(CollaborativeEditingEndpoint.class);

    /**concurrent包的线程安全Map，用来存放每个客户端对应的MyWebSocket对象。*/
    private static final ConcurrentHashMap<String,CollaborativeEditingEndpoint> webSocketMap = new ConcurrentHashMap<>();

    /**
     * 用于存放正在进行协同编辑的画布id和画布的在线编辑人数
     * String表示canvasId画布id
     * Integer表示画布的在线编辑人数
     */
    private static final ConcurrentHashMap<String,Integer> CECanvasCountMap = new ConcurrentHashMap<>();

    /**
     * 用于存放正在进行协同编辑的用户id及参与的画布id
     * 第一个String表示正在参与协同编辑的用户id
     * 第二个String表示该用户参与协同编辑的画布id
     */
    private static final ConcurrentHashMap<String,String> CECanvasUsersMap = new ConcurrentHashMap<>();

    /**与某个客户端的连接会话，需要通过它来给客户端发送数据 */
    private Session session;

    /*接收canvasId 画布id*/
    private String canvasId="";

    /**接收userId 用户id*/
    private String userId="";

    /**
    * 建立WebSocket连接成功的响应方法
    * @param session
    * @param config
    * @param canvasId
    */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config,
                       @PathParam("canvasId") String canvasId) {
        this.session = session;
        this.canvasId = canvasId;
        this.userId = String.valueOf(UserContext.getUser());
        //如果Map中已经存在该userId，那么就删除该用户id并重新存储
        if(webSocketMap.containsKey(userId)){
            webSocketMap.remove(userId);
            webSocketMap.put(userId,this);
        //如果Map中不存在该userId，那么就直接存储该用户id
        }else{
            webSocketMap.put(userId,this);
        }
        //判断该画布是否已经在Map中，如果存在，则该画布的在线编辑人数加一，如果不存在，则将该画布的在线编辑人数设置为1
        if(CECanvasCountMap.containsKey(canvasId)){
            int count = CECanvasCountMap.get(canvasId);
            CECanvasCountMap.remove(canvasId);
            CECanvasCountMap.put(canvasId, count + 1);
        }else{
            CECanvasCountMap.put(canvasId,1);
        }
        //将该用户id和该画布id存储到Map中
        if (CECanvasUsersMap.containsKey(userId)) {
            CECanvasUsersMap.remove(userId);
            CECanvasUsersMap.put(userId, canvasId);
        }else{
            CECanvasUsersMap.put(userId, canvasId);
        }
        log.info("用户:" + userId + ",参与到画布:" + canvasId + ",的协同编辑。当前协同编辑的人数为："
                + CECanvasCountMap.get(canvasId));
        try {
            sendMessage("连接成功");
        } catch (IOException e) {
            log.error("用户:" + userId + ",网络异常!!!!!!");
        }
        //广播消息，向当前正在进行协同编辑的画布中的所有用户发送消息
        String message = "用户:" + userId + ",加入画布:" + canvasId + ",的协同编辑";
        String messageVO = MessageUtils.getMessage(true, "系统消息", message);
        broadcastToCECanvas(messageVO);
    }

    /**
     * 向当前正在进行协同编辑的画布中的所有用户发送消息
     */
    private void broadcastToCECanvas(String message) {
        //遍历Map中的所有键值对，判断该画布id是否与当前正在进行协同编辑的画布id相同，如果相同，则向该用户id对应的WebSocket对象发送消息
        Set<Map.Entry<String, String>> entries = CECanvasUsersMap.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            //判断该画布id是否与当前正在进行协同编辑的画布id相同
            if (entry.getValue().equals(this.canvasId)) {
                try {
                    //若相同，则获取到正在协同编辑该画布的用户id
                    String userId = entry.getKey();
                    //获取到正在协同编辑该画布的用户id对应的WebSocket对象，并向该用户发送消息
                    CollaborativeEditingEndpoint cEEndpoint = webSocketMap.get(userId);
                    if (cEEndpoint != null && cEEndpoint.session != null) {
                        Session session = cEEndpoint.session;
                        // 使用 session 对象发送消息
                        session.getBasicRemote().sendText(message);
                    } else {
                        log.error("用户 {} 的 WebSocket 会话不存在", userId);
                    }
                } catch (IOException e) {
                    log.error("用户:" + userId + ",网络异常!!!!!!");
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
    * 接收到前端消息的响应方法
    * @param message
    */
    @OnMessage
    public void onMessage(String message) {
        log.info("用户消息:" + userId + ",报文:" + message);
        //可以群发消息
        //消息保存到数据库、redis
        if(StringUtils.isNotBlank(message)){
            try {
                //解析发送的报文
                JSONObject jsonObject = JSON.parseObject(message);
                //追加发送人(防止串改)
                jsonObject.put("fromUserId",this.userId);
                String toUserId=jsonObject.getString("toUserId");
                //传送给对应toUserId用户的websocket
                if(StringUtils.isNotBlank(toUserId) && webSocketMap.containsKey(toUserId)){
                    webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString());
                }else{
                    log.error("请求的userId:"+toUserId+"不在该服务器上");
                    //否则不在这个服务器上，发送到mysql或者redis
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    /**
     * 发送自定义消息
     * @param message
     */
    public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
        log.info("发送消息到:" + userId + "，报文:" + message);
        if(StringUtils.isNotBlank(userId) && webSocketMap.containsKey(userId)){
            webSocketMap.get(userId).sendMessage(message);
        }else{
            log.error("用户" + userId + ",不在线！");
        }
    }

    /**
    * 关闭WebSocket连接
    * @param session
    */
    @OnClose
    public void onClose(Session session) {
        if(webSocketMap.containsKey(userId)){
            //从Map中删除
            webSocketMap.remove(userId);
            //将该画布的在线编辑人数减一
            Integer count = CECanvasCountMap.get(canvasId);
            if (count > 1) {
                CECanvasCountMap.remove(canvasId);
                CECanvasCountMap.put(canvasId, count - 1);
            } else {
                CECanvasCountMap.remove(canvasId);
            }
            //将该画布的在线编辑用户从Map中删除
            CECanvasUsersMap.remove(userId);
        }
        log.info("用户退出在线编辑:" + userId);
    }

    /**
     * 报错时的响应方法
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 服务器发送消息到本用户
     * @param message
     */
    public void sendMessage(String message) throws IOException {
        //发送同步消息
        this.session.getBasicRemote().sendText(message);
    }

}
